// $Id: networkminsharealloc.cpp 63 2004-10-22 14:33:20Z jonas $
#include "networkminsharealloc.h"
#include "loghandler.h"

/**
 * Init members.
 *
 * @param nodes A pointer to a stl list of all nodes.
 */
NetworkMinShareAlloc::NetworkMinShareAlloc(list<Node *> *nodes)
	: Network(nodes)
{
}

/**
 * @todo Write documentation
 */
NetworkMinShareAlloc::~NetworkMinShareAlloc()
{
	/// @todo Delete m_nodes content.
}

/**
 * @todo Write documentation
 */
void NetworkMinShareAlloc::transfer()
{
	// Create node list containing incomming transfers as well
	for (list<Node *>::iterator node=m_nodes->begin(); node!=m_nodes->end(); node++) {
		m_nodesTransfersIn[(*node)] = 0;
	}
	// For all nodes...
	for (list<Node *>::iterator node=m_nodes->begin(); node!=m_nodes->end(); node++) {
		Transfer *transfer;
		int index = 0;

		// For all transfers (in nodes)
		while ((transfer = (*node)->getTransfer(index++))) {
			m_nodesTransfersIn[(Node *) transfer->m_destination]++;
		}
	}

	// DEBUG START
	/*
	cout << endl << "New batch" << endl;
	for (list<Node *>::iterator node=m_nodes->begin(); node!=m_nodes->end(); node++) {
		cout << m_nodesTransfersIn[(*node)] << ",";
	}
	cout << endl;
	*/
	// DEBUG END

	// Do transfers
	// For all nodes...
	for (list<Node *>::iterator node=m_nodes->begin(); node!=m_nodes->end(); node++) {
		Transfer *transfer;
		int index = 0;

		// For all transfers (in nodes)
		while ((transfer = (*node)->getTransfer(index++))) {
			// Only start if this is a root transfer
			if (transfer->m_isRootTransfer && (transfer->m_timeLastUsed < Clock::getInstance()->getTime() || transfer->m_timeLastUsed==4294967295U)) {
				transferChain(*node, transfer);
				//cout << endl;	// DEBUG
			}
		}
	}
}

void NetworkMinShareAlloc::transferChain(Node *node, Transfer *transfer)
{
	// Touch the transfer!
	transfer->m_timeLastUsed = Clock::getInstance()->getTime();

	// Find the file to write to
	StoredFile *file = ((Node *) transfer->m_destination)->getStoredFile((File *) transfer->m_file->m_file);
	assert(file);

	// Find our own maximum output speed
	int speedUp = node->getBandwidthUp() / node->getTransferCount();

	// Find the receivers maximum input speed
	int speedDown = ((Node *) transfer->m_destination)->getBandwidthDown() / m_nodesTransfersIn[(Node *) transfer->m_destination];

	// Find maximum bytes to transfer
	int maxBytes = transfer->m_file->m_size - file->m_size;

	// DEBUG START
	/*
	cout << "F(" << transfer->m_file->m_size << "," << file->m_size << ")";
	if(speedUp < min(maxBytes, speedDown)){
		cout << "U";
		cout << "(" << speedUp << ")";
	}
	else if(speedDown < min(maxBytes, speedUp)){
		cout << "D";
		cout << "(" << speedDown << ")";
	}
	else{
		cout << "B";
		cout << "(" << maxBytes << ")";
	}
	*/
	// DEBUG END

	// Transfer the minimum of all three
	int bytesCount = min(speedUp, speedDown);
	bytesCount = min(bytesCount, maxBytes);

	// Touch the active files
	file->touch();
	((StoredFile *) transfer->m_file)->touch();

	// Do the transfer and log it...
	file->m_size += bytesCount;
	LogHandler::getInstance()->network_transfer(node, transfer, bytesCount);

	// DEBUG START
	/*
	cout << "T(" << bytesCount << ")";
	if(transfer->m_isRootTransfer){
		cout << "[root]";
	}
	*/
	// DEBUG END

	// Continue the recursion as long as the next node in the chain is transfering the same file.
	Transfer *nextTransfer = ((Node *) transfer->m_destination)->getTransfer(file);
	if (nextTransfer) {
		// Is the transfer complete?
		if (file->m_size >= file->m_file->m_size) {
			nextTransfer->m_isRootTransfer = true;
			//cout << "[rootSet]";	// DEBUG
		}
		//cout << " > ";	// DEBUG
		//cout << ",";	// DEBUG
		transferChain((Node *) transfer->m_destination, nextTransfer);
	}
}
